package edu.njit.hz278.controller.staff;

import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import edu.njit.hz278.bean.Employee;
import edu.njit.hz278.bean.Nurse;
import edu.njit.hz278.bean.NurseInfo;
import edu.njit.hz278.bean.Physician;
import edu.njit.hz278.bean.PhysicianInfo;
import edu.njit.hz278.bean.Surgeon;
import edu.njit.hz278.bean.SurgeonInfo;
import edu.njit.hz278.controller.Constants;
import edu.njit.hz278.core.db.IQuery;
import edu.njit.hz278.core.db.Query;
import edu.njit.hz278.core.web.Get;
import edu.njit.hz278.core.web.ModelView;
import edu.njit.hz278.core.web.Param;
import edu.njit.hz278.core.web.Post;

/**
 * 
 * @author Rivers Zhang
 * 
 */
public final class Update {

	private static final IQuery<Employee> queryForStaff = new Query<>(
			Employee.class);
	private static final IQuery<NurseInfo> queryForNurse = new Query<>(
			NurseInfo.class);
	private static final IQuery<PhysicianInfo> queryForPhysician = new Query<>(
			PhysicianInfo.class);
	private static final IQuery<SurgeonInfo> queryForSurgeon = new Query<>(
			SurgeonInfo.class);

	private static final Logger log = LogManager.getLogger(Update.class);

	/**
	 * Inserts a staff.
	 * 
	 * @param staff
	 * @return
	 */
	@Post
	public Employee addStaff(
			@Param(value = "staff", forceValid = true) Employee staff) {
		staff.setEmpType(Employee.TYPE_STAFF);
		int empid = queryForStaff.insert(staff);
		staff.setEmpid(empid);
		return staff;
	}

	/**
	 * Removes a staff by @empid
	 * 
	 * @param empid
	 * @param mv
	 * @return
	 */
	@Get
	public ModelView rmStaff(@Param("empid") int empid, ModelView mv) {
		mv.put(Constants.MSG,
				queryForStaff.delete("empid", empid) == 0 ? Constants.MSG_OOPS
						: Constants.MSG_OK);
		return mv;
	}

	/**
	 * Inserts a nurse.
	 * 
	 * @param nurse
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	@Post
	public Nurse addNurse(@Param(value = "nurse", forceValid = true) Nurse nurse)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {

		Employee staff = new Employee();
		PropertyUtils.copyProperties(staff, nurse);
		staff.setEmpType(Employee.TYPE_NURSE);

		int empid;
		if ((empid = queryForStaff.insert(staff)) != 0) {
			NurseInfo ni = new NurseInfo();
			PropertyUtils.copyProperties(ni, nurse);
			ni.setEmpid(empid);
			queryForNurse.insert(ni);
			nurse.setEmpid(empid);
		}

		return nurse;
	}

	/**
	 * Removes a nurse by @empid.
	 * 
	 * @param empid
	 * @param mv
	 * @return
	 */
	@Get
	public ModelView rmNurse(@Param("empid") int empid, ModelView mv) {

		int affected = queryForStaff.delete("empid", empid)
				+ queryForNurse.delete("empid", empid);
		mv.put(Constants.MSG, affected == 2 ? Constants.MSG_OK
				: Constants.MSG_OOPS);

		return mv;
	}

	/**
	 * Inserts a physician.
	 * 
	 * @param phys
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	@Post
	public Physician addPhysician(
			@Param(value = "phys", forceValid = true) Physician phys)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {

		Employee staff = new Employee();
		PropertyUtils.copyProperties(staff, phys);
		staff.setEmpType(Employee.TYPE_PHYSICIAN);

		int empid;
		if ((empid = queryForStaff.insert(staff)) != 0) {
			PhysicianInfo pi = new PhysicianInfo();
			PropertyUtils.copyProperties(pi, phys);
			pi.setEmpid(empid);
			queryForPhysician.insert(pi);
			phys.setEmpid(empid);
		}

		return phys;
	}

	/**
	 * Removes a physician by @empid. FIXME no transaction.
	 * 
	 * @param empid
	 * @param mv
	 * @return
	 */
	@Get
	public ModelView rmPhysician(@Param("empid") int empid, ModelView mv) {
		int affected = queryForStaff.delete("empid", empid)
				+ queryForPhysician.delete("empid", empid);
		mv.put(Constants.MSG, affected == 2 ? Constants.MSG_OK
				: Constants.MSG_OOPS);

		return mv;
	}

	/**
	 * Inserts a surgeon.
	 * 
	 * @param surgeon
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	@Post
	public Surgeon addSurgeon(
			@Param(value = "surgeon", forceValid = true) Surgeon surgeon)
			throws IllegalAccessException, InvocationTargetException,
			NoSuchMethodException {
		log.debug(ToStringBuilder.reflectionToString(surgeon));
		Employee staff = new Employee();
		PropertyUtils.copyProperties(staff, surgeon);
		staff.setEmpType(Employee.TYPE_SURGEON);

		int empid;
		if ((empid = queryForStaff.insert(staff)) != 0) {
			PhysicianInfo pi = new PhysicianInfo();
			PropertyUtils.copyProperties(pi, surgeon);
			pi.setEmpid(empid);
			queryForPhysician.insert(pi);

			SurgeonInfo si = new SurgeonInfo();
			PropertyUtils.copyProperties(si, surgeon);
			si.setEmpid(empid);
			queryForSurgeon.insert(si);

			surgeon.setEmpid(empid);
		}

		return surgeon;
	}

	/**
	 * Removes a surgeon by @empid. FIXME no transaction.
	 * 
	 * @param empid
	 * @param mv
	 * @return
	 */
	@Get
	public ModelView rmSurgeon(@Param("empid") int empid, ModelView mv) {
		int affected = queryForStaff.delete("empid", empid)
				+ queryForPhysician.delete("empid", empid)
				+ queryForSurgeon.delete("empid", empid);
		mv.put(Constants.MSG, affected == 3 ? Constants.MSG_OK
				: Constants.MSG_OOPS);

		return mv;
	}

	/**
	 * Instead of visit different URLs for different Employees, this method
	 * provide a unified entry for deletion. It first query the database and
	 * check the type of the Employee. Then call the method according to the
	 * type. However, it may lead to a slow performance.
	 * 
	 * @param empid
	 * @param mv
	 * @return
	 */
	@Get
	public ModelView rm(@Param("empid") int empid, ModelView mv) {
		Employee staff = queryForStaff.getByKey("empid", empid);
		String type = staff.getEmpType();
		if (type.equals(Employee.TYPE_NURSE))
			return rmNurse(empid, mv);
		if (type.equals(Employee.TYPE_PHYSICIAN))
			return rmPhysician(empid, mv);
		if (type.equals(Employee.TYPE_SURGEON))
			return rmSurgeon(empid, mv);
		else
			return rmStaff(empid, mv);
	}
}
